// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.commons.utils;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.dengues.commons.i18n.Messages;
import org.eclipse.core.runtime.IProgressMonitor;

/**
 * Set of utility methods to process class names from jar files
 * 
 * 
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 2007-12-5 qiang.zhang $
 * 
 */
public class DriverUtil {

    /**
     * empty constructor
     */
    private DriverUtil() {
        // empty constructor
    }

    /**
     * Returns the number of classes in the jar. This is used for the progress monitor.
     * 
     * @param jarFile
     * @return
     * @throws Exception
     */
    public static int countClassesToProcess(File jarFile) throws Exception {
        JarFile jar = new JarFile(jarFile);
        return jar.getManifest().getEntries().size();
    }

    /**
     * Returns a string array of class names from the jar file.
     * 
     * @param jarFile
     * @param monitor
     * @return
     * @throws Exception
     */
    public static String[] getDriverClassesFromJar(File jarFile, IProgressMonitor monitor) throws Exception {
        ArrayList list = new ArrayList();
        String absolutePath = jarFile.getAbsolutePath();
        if (absolutePath.toLowerCase().endsWith(".dll")) { //$NON-NLS-1$
            String libpath = System.getProperty("java.library.path"); //$NON-NLS-1$
            String sep = System.getProperty("path.separator"); //$NON-NLS-1$
            String parentPath = jarFile.getParentFile().getAbsolutePath();
            String[] split = libpath.split(sep);
            boolean b = Arrays.asList(split).contains(parentPath);
            if (!b) {
                libpath = libpath + sep + parentPath;
                System.setProperty("java.library.path", libpath); //$NON-NLS-1$
            }
        } else {
            JarFile jar = new JarFile(jarFile);
            ZipFile zip = new ZipFile(jarFile);
            String taskDescription = Messages.getString("DriverUtil.taskName", //$NON-NLS-1$
                    new Object[] { jarFile.getName() });
            int max = zip.size();
            monitor.beginTask(taskDescription, max);
            Enumeration iter = jar.entries();
            while (iter.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) iter.nextElement();
                monitor.worked(1);
                if (entry.getName().endsWith(".class")) { //$NON-NLS-1$
                    try {
                        String name = entry.getName();
                        name = name.substring(0, name.length() - 6);
                        name = name.replace('/', '.');
                        Class testClass = getDriverClassesFromJar2(jarFile, name);
                        if (testClass != null) {
                            list.add(testClass.getName());
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return (String[]) list.toArray(new String[list.size()]);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDriverClassesFromJar2".
     * 
     * @param jarFile
     * @param classname
     * @return
     * @throws Exception
     */
    public static Class getDriverClassFromJars(String[] jarFile, String classname) throws Exception {
        Class outclass = null;
        for (String file : jarFile) {
            outclass = getDriverClassesFromJar2(new File(file), classname);
            if (outclass != null) {
                break;
            }
        }
        return outclass;
    }

    /**
     * Gets a Class instance from the jar of the supplied class name
     * 
     * @param jarFile
     * @param classname
     * @return
     * @throws Exception
     */
    private static Class getDriverClassesFromJar2(File jarFile, String classname) throws Exception {
        Class outclass = null;
        URL[] jdbcClasspathURL = new URL[1];
        jdbcClasspathURL[0] = jarFile.toURL();

        //
        // Create a new ClassLoader using the jdbcClasspathURL and attempt to
        // load the driver. Delegate to this class' ClassLoader if the class isn't
        // found.
        //
        try {
            //
            // Check this classloader's classpath for driver...
            //
            URLClassLoader classLoader = URLClassLoader.newInstance(jdbcClasspathURL);
            Class driverClass = classLoader.loadClass(Driver.class.getName());

            try {
                Class testclass = classLoader.loadClass(classname);

                if (driverClass.isAssignableFrom(testclass)) {
                    // we've got a JDBC driver
                    outclass = testclass;
                }
            } catch (ClassNotFoundException cnfe) {
                // ignore
            } catch (NoClassDefFoundError err) {
                // ignore
            }
        } catch (Exception e) {
            String reason = e.toString();
            Exception ce = new Exception(reason);
            ce.setStackTrace(e.getStackTrace());
            throw ce;
        }

        return outclass;
    }
}
